/*
 * $Id: JXStatusBar.java 4147 2012-02-01 17:13:24Z kschaefe $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.jdesktop.swingx;

import java.awt.Insets;

import javax.swing.JComponent;

import org.jdesktop.beans.JavaBean;
import org.jdesktop.swingx.plaf.LookAndFeelAddons;
import org.jdesktop.swingx.plaf.StatusBarAddon;
import org.jdesktop.swingx.plaf.StatusBarUI;

/**
 * <p>
 * A container for <code>JComponents</code> that is typically placed at the
 * bottom of a form and runs the entire width of the form. There are 3 important
 * functions that <code>JXStatusBar</code> provides. First,
 * <code>JXStatusBar</code> provides a hook for a pluggable look. There is a
 * definite look associated with status bars on windows, for instance. By
 * implementing a subclass of {@link JComponent}, we provide a way for the
 * pluggable look and feel system to modify the look of the status bar.
 * </p>
 *
 * <p>
 * Second, <code>JXStatusBar</code> comes with its own layout manager. Each item
 * is added to the <code>JXStatusBar</code> with a
 * <code>JXStatusBar.Constraint</code> as the constraint argument. The
 * <code>JXStatusBar.Constraint</code> contains an <code>Insets</code> object,
 * as well as a <code>ResizeBehavior</code>, which can be FIXED or FILL. The
 * resize behaviour applies to the width of components. All components added
 * will maintain there preferred height, and the height of the
 * <code>JXStatusBar</code> will be the height of the highest component plus
 * insets.
 * </p>
 * 
 * <p>
 * A constraint with <code>JXStatusBar.Constraint.ResizeBehavior.FIXED</code>
 * will cause the component to occupy a fixed area on the
 * <code>JXStatusBar</code>. The size of the area remains constant when the
 * <code>JXStatusBar</code> is resized. A constraint with this behavior may also
 * take a width value, see {@link JXStatusBar.Constraint#setFixedWidth(int)}.
 * The width is a preferred minimum width. If the component preferred width is
 * greater than the constraint width, the component width will apply.
 * </p>
 * 
 * <p>
 * All components with constraint
 * <code>JXStatusBar.Constraint.ResizeBehavior.FILL</code> will share equally
 * any spare space in the <code>JXStatusBar</code>. Spare space is that left
 * over after allowing for all FIXED component and the preferred width of FILL
 * components, plus insets
 * 
 * <p>
 * Constructing a <code>JXStatusBar</code> is very straightforward:
 * 
 * <pre>
 * <code>
 *      JXStatusBar bar = new JXStatusBar();
 *      JLabel statusLabel = new JLabel("Ready");
 *      JXStatusBar.Constraint c1 = new JXStatusBar.Constraint() 
 *      c1.setFixedWidth(100);
 *      bar.add(statusLabel, c1);     // Fixed width of 100 with no inserts
 *      JXStatusBar.Constraint c2 = new JXStatusBarConstraint(
 *              JXStatusBar.Constraint.ResizeBehavior.FILL) // Fill with no inserts
 *      JProgressBar pbar = new JProgressBar();
 *      bar.add(pbar, c2);            // Fill with no inserts - will use remaining space
 * </code>
 * </pre>
 * 
 * </p>
 *
 * <p>
 * Two common use cases for status bars include tracking application status and
 * progress. <code>JXStatusBar</code> does not manage these tasks, but instead
 * special components exist or can be created that do manage these tasks. For
 * example, if your application has a TaskManager or some other repository of
 * currently running jobs, you could easily create a TaskManagerProgressBar that
 * tracks those jobs. This component could then be added to the
 * <code>JXStatusBar</code> like any other component.
 * </p>
 *
 * <h2>Client Properties</h2>
 * <p>
 * The BasicStatusBarUI.AUTO_ADD_SEPARATOR client property can be specified,
 * which will disable the auto-adding of separators. In this case, you must add
 * your own JSeparator components. To use:
 * 
 * <pre>
 * <code>
 *      JXStatusBar sbar = new JXStatusBar();
 *      sbar.putClientProperty(BasicStatusBarUI.AUTO_ADD_SEPARATOR, false);
 *      sbar.add(comp1);
 *      sbar.add(new JSeparator(JSeparator.VERTICAL));
 *      sbar.add(comp2);
 *      sbar.add(comp3);
 *  </code>
 * </pre>
 * 
 * </p>
 *
 * @status REVIEWED
 *
 * @author pdoubleya
 * @author rbair
 * @author Karl George Schaefer
 */
@JavaBean
public class JXStatusBar extends JComponent {
	/**
	 * @see #getUIClassID
	 * @see #readObject
	 */
	public static final String uiClassID = "StatusBarUI";

	// TODO how to handle UI delegate setting of primitive?
	private boolean resizeHandleEnabled;

	/**
	 * Initialization that would ideally be moved into various look and feel
	 * classes.
	 */
	static {
		LookAndFeelAddons.contribute(new StatusBarAddon());
	}

	/**
	 * Creates a new JXStatusBar
	 */
	public JXStatusBar() {
		super();
		updateUI();
	}

	/**
	 * @param resizeHandleEnabled
	 *            the resizeHandleEnabled to set
	 */
	public void setResizeHandleEnabled(boolean resizeHandleEnabled) {
		boolean oldValue = isResizeHandleEnabled();
		this.resizeHandleEnabled = resizeHandleEnabled;
		firePropertyChange("resizeHandleEnabled", oldValue, isResizeHandleEnabled());
	}

	/**
	 * @return the resizeHandleEnabled
	 */
	public boolean isResizeHandleEnabled() {
		return resizeHandleEnabled;
	}

	/**
	 * Returns the look and feel (L&F) object that renders this component.
	 * 
	 * @return the StatusBarUI object that renders this component
	 */
	public StatusBarUI getUI() {
		return (StatusBarUI) ui;
	}

	/**
	 * Sets the look and feel (L&F) object that renders this component.
	 * 
	 * @param ui
	 *            the StatusBarUI L&F object
	 * @see javax.swing.UIDefaults#getUI
	 * @beaninfo bound: true hidden: true attribute: visualUpdate true
	 *           description: The component's look and feel delegate.
	 */
	public void setUI(StatusBarUI ui) {
		super.setUI(ui);
	}

	/**
	 * Returns a string that specifies the name of the L&F class that renders
	 * this component.
	 * 
	 * @return "StatusBarUI"
	 * @see javax.swing.JComponent#getUIClassID
	 * @see javax.swing.UIDefaults#getUI
	 * @beaninfo expert: true description: A string that specifies the name of
	 *           the L&F class.
	 */
	@Override
	public String getUIClassID() {
		return uiClassID;
	}

	/**
	 * Notification from the <code>UIManager</code> that the L&F has changed.
	 * Replaces the current UI object with the latest version from the
	 * <code>UIManager</code>.
	 * 
	 * @see javax.swing.JComponent#updateUI
	 */
	@Override
	public void updateUI() {
		setUI((StatusBarUI) LookAndFeelAddons.getUI(this, StatusBarUI.class));
	}

	/**
	 * The constraint object to be used with the <code>JXStatusBar</code>. It
	 * takes a ResizeBehaviour, Insets and a Width. Width is only applicable for
	 * ResizeBehavior.FIXED. @see JXStatusBar class documentation.
	 */
	public static class Constraint {
		public static enum ResizeBehavior {
			FILL, FIXED
		}

		private Insets insets;
		private ResizeBehavior resizeBehavior;
		private int fixedWidth = 0;

		/**
		 * Creates a new Constraint with default FIXED behaviour and no insets.
		 */
		public Constraint() {
			this(ResizeBehavior.FIXED, null);
		}

		/**
		 * Creates a new Constraint with default FIXED behaviour and the given
		 * insets
		 * 
		 * @param insets
		 *            may be null. If null, an Insets with 0 values will be
		 *            used.
		 */
		public Constraint(Insets insets) {
			this(ResizeBehavior.FIXED, insets);
		}

		/**
		 * Creates a new Constraint with default FIXED behaviour and the given
		 * fixed width.
		 * 
		 * @param fixedWidth
		 *            must be >= 0
		 */
		public Constraint(int fixedWidth) {
			this(fixedWidth, null);
		}

		/**
		 * Creates a new Constraint with default FIXED behaviour and the given
		 * fixed width, and using the given Insets.
		 * 
		 * @param fixedWidth
		 *            must be >= 0
		 * @param insets
		 *            may be null. If null, an Insets with 0 values will be
		 *            used.
		 */
		public Constraint(int fixedWidth, Insets insets) {
			if (fixedWidth < 0) {
				throw new IllegalArgumentException("fixedWidth must be >= 0");
			}
			this.fixedWidth = fixedWidth;
			this.insets = insets == null ? new Insets(0, 0, 0, 0) : (Insets) insets.clone();
			this.resizeBehavior = ResizeBehavior.FIXED;
		}

		/**
		 * Creates a new Constraint with the specified resize behaviour and no
		 * insets
		 * 
		 * @param resizeBehavior
		 *            - either JXStatusBar.Constraint.ResizeBehavior.FIXED or
		 *            JXStatusBar.Constraint.ResizeBehavior.FILL.
		 */
		public Constraint(ResizeBehavior resizeBehavior) {
			this(resizeBehavior, null);
		}

		/**
		 * Creates a new Constraint with the specified resize behavior and
		 * insets.
		 * 
		 * @param resizeBehavior
		 *            - either JXStatusBar.Constraint.ResizeBehavior.FIXED or
		 *            JXStatusBar.Constraints.ResizeBehavior.FILL.
		 * @param insets
		 *            may be null. If null, an Insets with 0 values will be
		 *            used.
		 */
		public Constraint(ResizeBehavior resizeBehavior, Insets insets) {
			this.resizeBehavior = resizeBehavior;
			this.insets = insets == null ? new Insets(0, 0, 0, 0) : (Insets) insets.clone();
		}

		/**
		 * Set the fixed width the component added with this constraint will
		 * occupy on the <code>JXStatusBar</code>. Only applies to
		 * ResizeBehavior.FIXED. Will be ignored for ResizeBehavior.FILL.
		 * 
		 * @param width
		 *            - minimum width component will occupy. If 0, the preferred
		 *            width of the component will be used. The width specified
		 *            must be >= 0
		 */
		public void setFixedWidth(int width) {
			if (width < 0) {
				throw new IllegalArgumentException("width must be >= 0");
			}
			fixedWidth = resizeBehavior == ResizeBehavior.FIXED ? width : 0;
		}

		/**
		 * Returns the ResizeBehavior.
		 * 
		 * @return ResizeBehavior
		 */
		public ResizeBehavior getResizeBehavior() {
			return resizeBehavior;
		}

		/**
		 * Returns the insets.
		 * 
		 * @return insets
		 */
		public Insets getInsets() {
			return (Insets) insets.clone();
		}

		/**
		 * Get fixed width. Width is zero for resize behavior FILLED
		 * 
		 * @return the width of this constraint
		 */
		public int getFixedWidth() {
			return fixedWidth;
		}
	}

}
